<?php
class SMTPMailer {
    private $host;
    private $port;
    private $username;
    private $password;
    private $socket;
    private $timeout = 30;
    private $debug = false;
    private $encrypt = 0;
    public $is_html = false;

    // 加密类型常量
    const ENCRYPT_NONE = 0;
    const ENCRYPT_SSL  = 1;
    const ENCRYPT_TLS  = 2;

    public function __construct($host, $port, $username, $password, $encrypt = 0) {
        $this->host = ($encrypt == self::ENCRYPT_SSL) ? "ssl://".$host : $host;
        $this->port = $port;
        $this->username = $username;
        $this->password = $password;
        $this->encrypt = $encrypt;
    }

    /**
     * 连接到SMTP服务器
     */
    private function connect() {
        $context = stream_context_create();
        $this->socket = stream_socket_client(
            $this->host.':'.$this->port,
            $errno,
            $errstr,
            $this->timeout,
            STREAM_CLIENT_CONNECT,
            $context
        );
        
        if (!$this->socket) {
            throw new Exception("连接失败: $errno - $errstr");
        }
        
        $response = fgets($this->socket);
        if (substr($response, 0, 3) != '220') {
            throw new Exception("服务器响应错误: $response");
        }
    }

    /**
     * SMTP认证
     */
    private function auth() {
        $this->sendCommand("EHLO ".$this->getDomain(), 250);
        
        if ($this->encrypt == self::ENCRYPT_TLS) {
            $this->sendCommand("STARTTLS", 220);
            if (!stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                throw new Exception("TLS握手失败");
            }
            $this->sendCommand("EHLO ".$this->getDomain(), 250);
        }

        $this->sendCommand("AUTH LOGIN", 334);
        $this->sendCommand(base64_encode($this->username), 334);
        $this->sendCommand(base64_encode($this->password), 235);
    }

    /**
     * 发送邮件
     * @param string|array $from 发件人，可以是字符串"email@example.com"或数组["name"=>"发件人名称", "email"=>"email@example.com"]
     * @param array|string $to 收件人，支持字符串或数组格式
     * @param string $subject 邮件主题
     * @param string $message 邮件内容
     * @param array $headers 额外的邮件头
     * @param array $attachments 附件
     * @return bool 是否发送成功
     */
    public function send($from, $to, $subject, $message, $headers = array(), $attachments = array()) {
        try {
            // 验证收件人
            $recipients = is_array($to) ? $to : explode(',',$to);
            $validRecipients = [];
            
            foreach ($recipients as $recipient) {
                $cleanRecipient = trim($recipient);
                if (!empty($cleanRecipient) && $this->validateEmail($cleanRecipient)) {
                    $validRecipients[] = $cleanRecipient;
                } else if ($this->debug) {
                    error_log("[SMTP WARNING] 无效的邮箱地址: $cleanRecipient");
                }
            }
            
            if (empty($validRecipients)) {
                throw new Exception("没有有效的收件人地址");
            }
            
            $this->connect();
            $this->auth();
            
            // 处理发件人格式
            $fromEmail = $from;
            $fromName = '';
            
            // 如果发件人是数组格式 ["name"=>"发件人名称", "email"=>"email@example.com"]
            if (is_array($from)) {
                $fromName = isset($from['name']) ? $from['name'] : '';
                $fromEmail = isset($from['email']) ? $from['email'] : $from[0];
            }
            
            // 验证发件人邮箱
            if (!$this->validateEmail($fromEmail)) {
                throw new Exception("发件人邮箱地址无效: $fromEmail");
            }
            
            // 设置发件人
            $this->sendCommand("MAIL FROM: <$fromEmail>", 250);
            
            // 设置收件人 - 确保每个收件人单独处理
            foreach ($validRecipients as $recipient) {
                try {
                    $this->sendCommand("RCPT TO: <$recipient>", 250);
                } catch (Exception $e) {
                    if ($this->debug) {
                        error_log("[SMTP WARNING] 收件人 $recipient 被服务器拒绝: " . $e->getMessage());
                    }
                    // 如果只有一个收件人且失败，则抛出异常
                    if (count($validRecipients) == 1) {
                        throw $e;
                    }
                    // 多个收件人时，继续处理其他收件人
                }
            }
            
            // 发送数据
            $this->sendCommand("DATA", 354);
             // 生成MIME边界
            $boundary = md5(uniqid());
            // 构建邮件头
            // 处理From头，确保符合RFC标准
            $fromHeader = $fromEmail;
            if (is_array($from)) {
                if (!empty($from['name'])) {
                    $fromHeader = $this->encodeHeader($from['name']) . " <" . $fromEmail . ">";
                }
            } else if (strpos($from, '@') !== false && strpos($from, '<') === false) {
                // 如果只有邮箱地址，添加一个默认名称（使用@前面的部分）
                $namePart = explode('@', $fromEmail)[0];
                $fromHeader = $this->encodeHeader($namePart) . " <" . $fromEmail . ">";
            }
            
            $headers = array_merge(array(
                "From: " . $fromHeader,
                "To: ".$this->encodeHeader(implode(", ", $validRecipients)),
                "Subject: ".$this->encodeHeader($subject),
                "Date: ".date('r'),
                "MIME-Version: 1.0",
                "Content-Type: multipart/alternative; boundary=\"$boundary\""
            ), $headers);
            
            // 构建邮件体
            $body = "--$boundary\r\n";
            if($this->is_html){
                $body .= "Content-Type: text/html; charset=UTF-8\r\n";
            }else{
                $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
            }
            $body .= "Content-Transfer-Encoding: base64\r\n\r\n";
            $body .= chunk_split(base64_encode($message))."\r\n";
            
            // 添加附件
            if(!empty($attachments)){
            foreach ($attachments as $file) {
                if (!file_exists($file)) continue;
                
                $content = chunk_split(base64_encode(file_get_contents($file)));
                $filename = $this->encodeHeader(basename($file));
                $body .= "--$boundary\r\n";
                $body .= "Content-Type: application/octet-stream\r\n";
                $body .= "Content-Transfer-Encoding: base64\r\n";
                $body .= "Content-Disposition: attachment; filename=\"$filename\"\r\n\r\n";
                $body .= "$content\r\n";
            }}
            
            $body .= "--$boundary--\r\n";
            
            // 发送完整邮件
            $this->sendCommand(implode("\r\n", $headers)."\r\n\r\n".$body."\r\n.", 250);
            
            $this->disconnect();
            return true;
        } catch (Exception $e) {
            if ($this->debug) error_log('[SMTP ERROR] '.$e->getMessage());
            return false;
        }
    }

    /**
     * 发送SMTP命令
     */
    private function sendCommand($command, $expectedCode) {
        fwrite($this->socket, $command."\r\n");
        $response = $this->getResponse();
        
        if (substr($response, 0, 3) != $expectedCode) {
            throw new Exception("命令错误: $command - 响应: $response");
        }
        return $response;
    }

    /**
     * 获取完整响应
     */
    private function getResponse() {
        $data = '';
        while ($str = fgets($this->socket, 4096)) {
            $data .= $str;
            if (substr($str, 3, 1) == ' ') break;
        }
        return $data;
    }

    /**
     * 断开连接
     */
    private function disconnect() {
        $this->sendCommand("QUIT", 221);
        if (is_resource($this->socket)) {
            fclose($this->socket);
        }
    }

    /**
     * 编码邮件头
     */
    private function encodeHeader($str) {
        return '=?UTF-8?B?'.base64_encode($str).'?=';
    }

    /**
     * 获取当前域名
     */
    private function getDomain() {
        if (isset($_SERVER['SERVER_NAME'])) {
            return $_SERVER['SERVER_NAME'];
        }
        return 'localhost';
    }

    /**
     * 验证邮箱地址格式
     */
    private function validateEmail($email) {
        return filter_var(trim($email), FILTER_VALIDATE_EMAIL) !== false;
    }

    /**
     * 设置调试模式
     */
    public function setDebug($debug) {
        $this->debug = (bool)$debug;
    }
}